from typing import TypeVar, Generic, List, Dict, Set, Iterator

from chernc.treediff.gumtree.matchers.mapping import Mapping
from chernc.treediff.gumtree.tree.tree import TreeLike

# 定义泛型类型变量
T = TypeVar("T", bound="TreeLike")  # 泛型类型 T，表示树的节点类型

class MappingStore(Generic[T]):
    def __init__(self, src: T, dst: T) -> None:
        """
        Instantiate a new mapping store between the provided source and destination nodes.
        :param src: Root of the source node
        :param dst: Root of the destination node
        """
        self.src: T = src
        self.dst: T = dst
        self.src_to_dst: Dict[T, T] = {}
        self.dst_to_src: Dict[T, T] = {}

    def size(self) -> int:
        """Return the number of mappings."""
        return len(self.src_to_dst)

    def as_set(self) -> Set[Mapping[T]]:
        """Convert the mapping store to a set of mappings."""
        mappings: Set[Mapping[T]] = set()
        for src in self.src_to_dst:
            mappings.add(Mapping(src, self.src_to_dst[src]))
        return mappings

    def add_mapping(self, src: T, dst: T) -> None:
        """Add a mapping between the two provided nodes inside the mapping store."""
        self.src_to_dst[src] = dst
        self.dst_to_src[dst] = src

    def add_mapping_recursively(self, src: T, dst: T) -> None:
        """Add a mapping between the two provided nodes and their descendants."""
        self.add_mapping(src, dst)
        for src_child, dst_child in zip(src.get_children(), dst.get_children()):
            self.add_mapping_recursively(src_child, dst_child)  # type: ignore

    def remove_mapping(self, src: T, dst: T) -> None:
        """Remove the mapping between the provided source and destination nodes."""
        if src in self.src_to_dst:
            del self.src_to_dst[src]
        if dst in self.dst_to_src:
            del self.dst_to_src[dst]

    def get_dst_for_src(self, src: T) -> T | None:
        """Return the destination source node mapped to the given source node."""
        return self.src_to_dst.get(src)

    def get_src_for_dst(self, dst: T) -> T | None:
        """Return the source node mapped to the given destination node."""
        return self.dst_to_src.get(dst)

    def is_src_mapped(self, src: T) -> bool:
        """Return whether or not there is a mapping for the given source node."""
        return src in self.src_to_dst

    def is_dst_mapped(self, dst: T) -> bool:
        """Return whether or not there is a mapping for the given destination node."""
        return dst in self.dst_to_src

    def are_both_unmapped(self, src: T, dst: T) -> bool:
        """Return whether or not the given source and destination nodes are unmapped."""
        return not (self.is_src_mapped(src) or self.is_dst_mapped(dst))

    def are_srcs_unmapped(self, srcs: List[T]) -> bool:
        """Return whether or not all the given source nodes are unmapped."""
        return all(not self.is_src_mapped(src) for src in srcs)

    def are_dsts_unmapped(self, dsts: List[T]) -> bool:
        """Return whether or not all the given destination nodes are unmapped."""
        return all(not self.is_dst_mapped(dst) for dst in dsts)

    def has_unmapped_src_children(self, t: T) -> bool:
        """Return whether or not the given source node has unmapped descendants."""
        return any(not self.is_src_mapped(child) for child in t.get_descendants())  # type: ignore

    def has_unmapped_dst_children(self, t: T) -> bool:
        """Return whether or not the given destination node has unmapped descendants."""
        return any(not self.is_dst_mapped(child) for child in t.get_descendants())  # type: ignore

    def has(self, src: T, dst: T) -> bool:
        """Return whether or not there is a mapping between the given source and destination nodes."""
        return self.src_to_dst.get(src) == dst

    def is_mapping_allowed(self, src: T, dst: T) -> bool:
        """Return whether or not a mapping is possible between the provided source and destination nodes."""
        return src.has_same_type(dst) and self.are_both_unmapped(src, dst)

    def __iter__(self) -> Iterator[Mapping[T]]:
        """Make MappingStore iterable."""
        return iter(self.as_set())

    def __repr__(self) -> str:
        """String representation of the MappingStore."""
        return "\n".join(str(mapping) for mapping in self)
